package com.wpen.unit.core.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import com.wpen.unit.core.service.BatchScheduleHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.wpen.unit.core.cache.CacheDatas;
import com.wpen.unit.core.ddo.TriggerDo;
import com.wpen.unit.core.service.TriggerPoolHelper;
import com.wpen.unit.cron.CronUtil;
import com.wpen.unit.enums.PlanType;
import com.wpen.unit.enums.YN;
import com.wpen.unit.infra.mybatis.job.po.BatchInfoPo;
import com.wpen.unit.infra.mybatis.job.po.BatchPlanPo;
import com.wpen.unit.infra.mybatis.job.po.LockPo;
import com.wpen.unit.infra.repository.job.BatchPlanRepository;
import com.wpen.unit.infra.repository.job.LockRepository;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * @author Wang Peng
 * @date 2022-12-06
 */
@Service
@Slf4j
public class BatchScheduleHandlerImpl implements BatchScheduleHandler {
    private final static String LOCK_NAME = "unit_job_batch_schedule";
    @Autowired
    private LockRepository lockRepository;
    @Autowired
    private BatchPlanRepository batchPlanRepository;
    @Autowired
    private CacheDatas cacheDatas;
    @Autowired
    private TriggerPoolHelper triggerPoolHelper;

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void scheduleHandle(String acctDate, long nowTime, int onceSize, long loopReadTime,
            Map<Integer, List<TriggerDo>> delayData) {
        // 锁表
        LockPo lockPo = lockRepository.queryForUpdate(LOCK_NAME);
        if (lockPo == null) {
            lockPo = new LockPo();
            lockPo.setLockName(LOCK_NAME);
            lockRepository.insert(lockPo);
            lockPo = lockRepository.queryForUpdate(LOCK_NAME);
        }

        // 查询
        long queryTime = nowTime + loopReadTime;
        List<BatchPlanPo> schedulePlanPos = batchPlanRepository.queryListByNextTime(onceSize, queryTime);
        // 加工处理
        if (schedulePlanPos == null || schedulePlanPos.isEmpty()) {
            return;
        }
        for (BatchPlanPo batchPlanPo : schedulePlanPos) {
            // 一条条记录分析
            try {
                BatchInfoPo batchInfoPo = cacheDatas.getBatchInfoByCode(batchPlanPo.getBizCode(), batchPlanPo.getBatchCode());
                String desc = batchInfoPo.getBatchCode() + "-" + batchInfoPo.getBatchName();
                log.info("Batch[" + desc + "] execute quickly!");
                // TODO 判断是否超过最晚执行时间 这个判断后面再想想
                // TODO 检测依赖是否完成，如果依赖没有完成，等待
                // 执行时间判断
                TriggerDo triggerDo = new TriggerDo();
                triggerDo.setAcctDate(acctDate);
                triggerDo.setBizCode(batchInfoPo.getBizCode());
                triggerDo.setBatchCode(batchInfoPo.getBatchCode());
                
                triggerDo.setPlanType(batchPlanPo.getPlanType());
                triggerDo.setNowTime(nowTime);
                triggerDo.setBatchTriggerTime(batchPlanPo.getTriggerNextTime());
                triggerDo.setTriggerNextTime(batchPlanPo.getTriggerNextTime());
                triggerDo.setDesc(desc);

                if (nowTime >= batchPlanPo.getTriggerNextTime()) {
                    // 如果 任务的执行时间 小于等于 当前时间，，立刻执行
                    // 先按立刻执行处理
                    triggerPoolHelper.triggerBatch(triggerDo);
                } else {
                    // 不到执行时刻点，，延迟执行
                    int delaySecond = (int) ((batchPlanPo.getTriggerNextTime() / 1000) % 60);
                    if (log.isDebugEnabled()) {
                        log.debug("Batch Add => " + delaySecond + ">" + triggerDo);
                    }
                    List<TriggerDo> items = delayData.get(delaySecond);
                    if (items == null) {
                        items = new ArrayList<>();
                        delayData.put(delaySecond, items);
                    }
                    items.add(triggerDo);
                }
                // 判断：执行计划的类型，如果是手工任务，关闭任务即可。
                if (PlanType.Manual.value().equals(batchPlanPo.getPlanType())) {
                    batchPlanPo.setEnabled(YN.No.value());
                } else {
                    // 重新计算下一次执行时间，从本次查询的时钟往后推
                    Date nextTime = CronUtil.getTime(batchInfoPo.getScheduleConf(), new Date(queryTime));
                    if (nextTime != null) {
                        batchPlanPo.setTriggerLastTime(batchPlanPo.getTriggerNextTime());
                        batchPlanPo.setTriggerNextTime(nextTime.getTime());
                    } else {
                        log.error("refresh nextTime fail for job: " + batchInfoPo.getId() + ": "
                                + batchInfoPo.getScheduleConf());
                        batchPlanPo.setTriggerLastTime(0L);
                        batchPlanPo.setTriggerNextTime(0L);
                        batchPlanPo.setEnabled(YN.No.value());
                    }
                }
                // 更新数据库，下一次执行时间
                batchPlanRepository.updateById(batchPlanPo);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                // TODO 推送日志到监控通知
            }
        }

    }

}
